Расширение GLSL в последних версиях OpenGL

В этой статье будут рассмотрены некоторые расширения, добавленные в язык GLSL, в версиях OpenGL 3.x и 4.x.

Расширение ARB_explicit_attrib_location

Данное расширение, вошедшее в состав OpenGL 3.2, предлагает новый способ связывания данных для атрибутов и вершинной программы. Традиционный способ задания вершинных атрибутов заключается в использовании имен. При этом код приложения по имени атрибута получает его положение (location, индекс) и уже именно по этому положению и задает значения для данного атрибута.

Расширение ARB_explicit_attrib_location позволяет прямо в вершинном шейдере задать положение для атрибута. Тогда вызывающая программа может напрямую использовать именно это значение не запрашивая его (хотя запрос нормально отработает, но он вернет именно то значение, которое было указано в шейдере).

В частности подобная возможность позволяет изначально привязать к номерам определенные виды атрибутов для целой группы шейдеров и тем самым получить унифицированный (и более простой) способ задания атрибутов для всей этой группы.

Для задания положения атрибутов в GLSL была введена директива layout, выглядящая следующим образом:

layout (location = 3) in vec4 normal;      // задаем нормаль с положением 3
layout (location = 0) in vec4 position;    // задаем атрибут с положением 0
layout (location = 1) in vec4 colors [2];  // задаем сразу два цвета с положениями 1 и 2

При этом число задает номер для векторного атрибута. Так декларация для атрибута colors резервирует сразу два положения - 1 и 2 - в которых будут размещены атрибуты colors [0] и colors [1].

Кроме того директива layout может использоваться и во фрагментном шейдере. Там она задает какой именно номер будет использован для заданной выходной переменной (т.е. играет роль индекса в переменной gl_FragData в OpenGL 2.0).

Так если у нас имеется два выходных вектора c1 и c2, то для того, чтобы задать в какую именно выходную текстуру они должны записываться, можно использовать следующие директивы.

layout (location = 0) out vec4 c1;
layout (location = 1) out vec4 c2;

Поскольку расширение ARB_blend_func_extended позволяет для альфа-блендинга использовать два выдаваемых фрагментным шейдером цвета (один из них выступает как source, а второй используется как коэффициент смешение source и dest цветов), то в директиве layout для фрагментного шейдера можно кроме положения выходного значения указать также и индекс, с которым он будет участвовать в уравнении смешения цветов.

layout (location = 0, index = 1) out vec4 c1;
layout (location = 1, index = 0) out vec4 c2;

Если в директиве layout индекс не указан, то он считается равным нулю.

Узнать максимальный номер для положения вершинного атрибута можно при помощи следующего фрагмента кода.

int maxAttribs;
    
glGetIntegerv ( GL_MAX_VERTEX_ATTRIBUTES, &maxAttribs );

Использование директивы layout для задания способа хранения матриц в uniform-блоках

Расширение ARB_uniform_buffer_object добавляет еще одно возможное использование для рассмотренной выше директивы layout - задания того, каким образом матрица должна храниться внутри uniform-блока - по строкам или по столбцам.

uniform MyBlock
{
 . . .
 layout (row_major)    mat4 m1;
 layout (column_major) mat4 m2;
 . . .
};

Таким образом можно явно указать способ, в котором матрица будет положена в uniform-буфер и использовать внутри программы тот формат матрицы, который более удобен.

Расширение ARB_shader_bit_encoding

Данное расширение вводит специальные функции, которые позволяют представить биты, задающие 32-битовое число с плавающей точкой как целое число (со знаком или без) и наоборот, по набору битов, заданному целым числом, получить соответствующее число с плавающей точкой.

Ниже перечисляются введенные функции, через genIType обозначен любой из типов int, ivec2, ivec3 и ivec4, через genUType - любой из типов uint, uvec2, uvec3 и uvec4 и через genType - любой из типов float, vec2, vec3 и vec4.

genIType floatBitsToInt  ( genType value  );
genUType floatBitsToUint ( genType value  );
genType  intBitsToFloat  ( genIType value );
genType  uintBitsToFloat ( genUType value );

Данные функции работают как со скалярными аргументами, так и с векторами.

Расширение ARB_texture_query_lod

Когда фрагментный шейдер обращается к текстуре, то вычисляется так называемый LOD и используется для чтения из текстуры в соответствии с выбранным режимом фильтрацию LOD (Level-Of-Detail) определяется исходя из коэффициента масштабирования по следующим формулам:

Однако вычисление LOD происходит автоматически и обычно полученное значение недоступно программе. Данное расширение дает возможность для каждого типа сэмплера узнать используемый уровень LOD. При этом результатом такого запроса является величина типа vec2.

Вторая (y) компонента результата содержит непосредственно значение .

Первая (x) компонента результата несет в себе информацию об обращении к массиву mipmap-уровней. Если идет обращение всего к одному слою пирамиды, то возвращается номер этого уровня по отношению к базовому. Если задействованы сразу два соседних слоя пирамиды, то появляется дробная часть, определяющая вклад каждого из этих уровней.

Приводимая ниже функция computeAccessedLod иллюстрирует вычисление возвращаемого значения x-компоненты.

float computeAccessedLod ( float lambdaPrime )
{
    float lod = clamp ( lambdaPrime, TEXTURE_MIN_LOD, TEXTURE_MAX_LOD );

    if ( lod < 0.0 )
        lod = 0.0;
    
    if ( lod > (float) maxAccessibleLevel )
        lod = (float) maxAccessibleLevel;
    
    if ( TEXTURE_MIN_FILTER == GL_LINEAR || TEXTURE_MIN_FILTER == GL_NEAREST )
        return 0.0;
   
    if ( TEXTURE_MIN_FILTER == NEAREST_MIPMAP_NEAREST || TEXTURE_MIN_FILTER == LINEAR_MIPMAP_LINEAR )
        return ceil ( lod + 0.5 ) - 1.0;
    
    return lod;
}

Для непосредственного получения уровня LOD из шейдера служат следующие вводимые в GLSL функции.

vec2 textureQueryLOD ( gsampler1D sampler, float coord );
vec2 textureQueryLOD ( gsampler2D sampler, vec2 coord );
vec2 textureQueryLOD ( gsampler3D sampler, vec3 coord );
vec2 textureQueryLOD ( gsamplerCube sampler, vec3 coord );
vec2 textureQueryLOD ( gsampler1DArray sampler, float coord );
vec2 textureQueryLOD ( gsampler2DArray sampler, vec2 coord );
vec2 textureQueryLOD ( gsamplerCubeArray sampler, vec3 coord );
vec2 textureQueryLOD ( sampler1DShadow sampler, float coord );
vec2 textureQueryLOD ( sampler2DShadow sampler, vec2 coord );
vec2 textureQueryLOD ( samplerCubeShadow sampler, vec3 coord );
vec2 textureQueryLOD ( sampler1DArrayShadow sampler, float coord );
vec2 textureQueryLOD ( sampler2DArrayShadow sampler, vec2 coord );
vec2 textureQueryLOD ( samplerCubeArrayShadow sampler, vec3 coord );

Расширение ARB_fragment_coord_conventions

Координаты фрагментов (значение gl_FragCoord.xy) в различных API задаются разными способами. При этом различие заключается как в том, какой именно угол окна (левый нижний или левый верхний) считается началом координат, так и в том, являются ли координаты фрагментов целыми числами или же дробными (с дробной частью равно 0.5).

Рис 1. Задание системы координат в окне для OpenGL и Direct3D.

Рис 2. Варианты задания координат пикселов.

Традиционно OpenGL считает, что начало координат расположено в левом нижнем углу и координаты фрагментов имеют дробную часть, равную 0.5. Однако для Direct3D 9 и ниже координаты фрагментов являются целыми числами и точка с координатами (0,0) соответствует верхнему левому углу окна.

Для Direct3D 10 и выше начало также координат расположено в верхнем левом углу (как в D3D 9), но координаты фрагментов являются числами, с дробной частью, равно 0.5 (как в OpenGL).

Расширение ARB_fragment_coord_conventions позволяет задать для gl_FragCoord любой из указанных вариантов. Для этого используется директива layout, как показано на примерах ниже.

layout (origin_upper_left) in vec4 gl_FragCoord;
layout (pixel_center_integer) in vec4 gl_FragCoord;
layout (origin_upper_left, pixel_center_integer) in vec4 gl_ForCoord;

По умолчанию используется соглашения, традиционные для OpenGL (нижний левый угол и дробные координаты).

Проверка чисел на NaN и Inf

Довольно часто возникающей проблемой в шейдерах является появляющиеся в результате расчетов значения NaN и Inf. Были введены специальные функции, позволяющие проверить является ли заданное скалярное или векторное значение NaN или Inf. Для этого служат следующие функции:

genBType isnan ( genType value  );
genBType isinf ( genType value  );

Используются технологии uCoz